/**
 * @file app\protocol\mobus\inc\protocol_mobus.h
 *
 * Copyright (C) 2023
 *
 * protocol_mobus.h is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author HinsShum hinsshum@qq.com
 *
 * @encoding utf-8
 */
#ifndef __PROTOCOL_MOBUS_H
#define __PROTOCOL_MOBUS_H

#ifdef __cplusplus
extern "C" {
#endif

    /*---------- includes ----------*/
#include "protocol_mobus_def.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/*---------- macro ----------*/
/*---------- type define ----------*/
    typedef union protocol_mobus* protocol_mobus_t;
    union __attribute__((aligned(1))) protocol_mobus {
        struct {
            uint8_t type;
            uint8_t sync_hi;
            uint8_t dest_addr;
            uint8_t sync_lo;
            uint8_t src_addr;
            uint8_t sub_channel : 4;
            uint8_t number_of_subs : 4;
            uint8_t payload_length;
            uint8_t payload[0];
        } common;
        struct {
            uint8_t type;
            uint8_t sync_hi;
            uint8_t dest_addr;
            uint8_t sync_lo;
            uint8_t src_addr;
            uint8_t cmd;
            uint8_t payload_length;
            uint8_t payload[0];
        } broadcast;
        struct {
            uint8_t type;
            uint8_t sync_hi;
            uint8_t dest_addr;
            uint8_t sync_lo;
            uint8_t src_addr;
            uint8_t cmd;
            uint8_t payload_length;
            uint8_t payload[0];
        } query;
        struct {
            uint8_t type;
            uint8_t sync_hi;
            uint8_t dest_orgroup_addr;
            uint8_t sync_lo;
            uint8_t src_orgroup_addr;
            uint8_t sub_channel : 4;
            uint8_t number_of_subs : 4;
            uint8_t payload_length;
            uint8_t payload[0];
        } group;
        struct {
            uint8_t type;
            uint8_t sync_hi;
            uint8_t dest_addr;
            uint8_t sync_lo;
            uint8_t src_addr;
            uint8_t version;
            uint8_t mobus_type_hi;
            uint8_t mobus_type_lo;
            uint8_t sub_channel : 4;
            uint8_t number_of_subs : 4;
            uint8_t payload_length;
            uint8_t payload[0]; // 通道总数
        } login;
        struct {
            uint8_t type;
            uint8_t sync_hi;
            uint8_t dest_addr;
            uint8_t sync_lo;
            uint8_t src_addr;
            uint8_t cmd;
            uint8_t payload_length;
            uint8_t payload[0];
        } ack;
    };

    typedef struct protocol_mobus_evt* protocol_mobus_evt_t;
    struct protocol_mobus_evt {
        enum {
            PROTOCOL_MOBUS_EVT_TYPE_UNICAST,
            PROTOCOL_MOBUS_EVT_TYPE_MULTICAST,
            PROTOCOL_MOBUS_EVT_TYPE_BROADCAST,
            PROTOCOL_MOBUS_EVT_TYPE_GROUP,
            PROTOCOL_MOBUS_EVT_TYPE_ACK
        } evt_type;
        mobus_frame_type_t frame_type;
        union {
            struct {
                mobus_cmd_t cmd;
                uint8_t* data;
                uint16_t length;
            } broadcast;
            struct {
                uint16_t uid;
                uint8_t* data;
                uint16_t length;
            } multicast;
            struct {
                mobus_cmd_t cmd; /*<< used for query frame */
                uint8_t native_addr;
                uint8_t sync;
                uint8_t number_of_channels;
                uint8_t sub_channel;
                uint8_t* data;
                uint16_t length;
            } unicast;
            struct {
                mobus_frame_type_t send_frame_type;
                uint8_t native_addr;
                uint8_t* data;
                uint16_t length;
            } ack;
        };
    };

    struct protocol_mobus_ops {
        void (*send)(const uint8_t* pbuf, uint32_t length);
        void (*push)(const uint8_t* pbuf, uint32_t length, uint32_t retrans);
        void (*stop_retrans)(void);
        bool (*evt_cb)(protocol_mobus_evt_t evt, mobus_ack_result_t* result);
    };

    /*---------- variable prototype ----------*/
    /*---------- function prototype ----------*/
    extern void protocol_mobus_init(struct protocol_mobus_ops* pops);
    extern void protocol_mobus_login_with_channel_type(
        uint8_t addr, uint8_t version, mobus_device_type_t type,
        uint32_t number_of_channels, mobus_device_type_t* channels_type);
    extern void protocol_mobus_ack(uint8_t addr, uint8_t sync_word,
        mobus_ack_result_t result);
    extern void protocol_mobus_parse(const uint8_t* recv_buf, uint32_t recv_length,
        const uint8_t* trans_buf,
        uint32_t trans_length);
    extern void protocol_mobus_upload(mobus_frame_type_t type, uint8_t addr,
        uint8_t number_of_channels,
        uint8_t sub_channel, const uint8_t* pbuf,
        uint8_t length);
    extern void protocol_mobus_login_with_channel_type_over_8_channles(
        uint8_t addr, uint8_t version, mobus_device_type_t type,
        uint16_t channel_nums, mobus_device_type_t* channels_type);
    extern void protocol_mobus_dev_report(mobus_frame_type_t type,
        uint8_t sub_channel, const uint8_t* pbuf,
        uint8_t length);
    extern void protocol_mobus_Dev_forwarding_protocol(mobus_frame_type_t type, uint8_t sub_channel,
        const uint8_t* pbuf, uint8_t length);
#ifdef __cplusplus
}
#endif
#endif /* __PROTOCOL_MOBUS_H */
